import _ from 'lodash'
import Config from 'app/config.js'

const ROOT_PATH = ''
const nodeSorter = (a, b) => {
  // node.isDir comes first
  // then sort by node.path alphabetically
  if (a.isDir && !b.isDir) return -1
  if (!a.isDir && b.isDir) return 1
  if (a.path < b.path) return -1
  if (a.path > b.path) return 1
  return 0
}

class FileNode {
  static get root () {
    return FileNode.nodes[ROOT_PATH]
  }

  constructor (nodeInfo) {
    const {
      name,
      path,
      isDir,
      gitStatus,
      isFolded,
      isFocused,
      isHighlighted,
      contentType,
      size
    } = nodeInfo

    this.name = name
    this.path = path
    this.isDir = isDir
    this.gitStatus = gitStatus
    this.isFolded = _.isBoolean(isFolded) ? isFolded : true
    this.isFocused = _.isBoolean(isFocused) ? isFocused : false
    this.isHighlighted = _.isBoolean(isHighlighted) ? isHighlighted : false
    this.contentType = contentType
    this.size = size
  }

  reconstruct () {
    FileNode.nodes[this.path] = new FileNode(this)
  }

  get isRoot () {
    return this.path === ROOT_PATH
  }

  get depth () {
    var slashMatches = this.path.match(/\/(?=.)/g)
    return slashMatches ? slashMatches.length : 0
  }

  get parent () {
    const pathComps = this.path.split('/')
    pathComps.pop()
    const parentPath = pathComps.join('/')
    return FileNode.nodes[parentPath]
  }

  get children () {
    const depth = this.depth
    let children = Object.values(FileNode.nodes)
      .filter(node => node.path.startsWith(`${this.path}/`) && node.depth === depth + 1)
      .sort(nodeSorter)
    return children
  }

  get siblings () {
    return this.parent.children
  }

  get firstChild () {
    return this.children[0]
  }

  get lastChild () {
    return this.children.pop()
  }

  get lastVisibleDescendant () {
    var lastChild = this.lastChild
    if (!lastChild) return this
    if (!lastChild.isDir) return lastChild
    if (lastChild.isFolded) return lastChild
    return lastChild.lastVisibleDescendant
  }

  prev (jump) {
    if (this.isRoot) return this

    const siblings = this.siblings
    const prevNode = siblings[siblings.indexOf(this) - 1]

    if (prevNode) {
      if (!jump) return prevNode
      if (!prevNode.isDir || prevNode.isFolded) return prevNode
      if (prevNode.lastChild) {
        return prevNode.lastVisibleDescendant
      } else {
        return prevNode
      }
    } else {
      return this.parent
    }
  }

  next (jump) {
    if (jump && this.isDir && !this.isFolded) {
      if (this.firstChild) return this.firstChild
    } else if (this.isRoot) {
      return this
    }

    const siblings = this.siblings
    const nextNode = siblings[siblings.indexOf(this) + 1]

    if (nextNode) {
      return nextNode
    } else {
      if (this.parent.isRoot) return this
      return this.parent.next()
    }
  }

  forEachDescendant (handler) {
    if (!this.isDir) return
    this.children.forEach(childNode => {
      handler(childNode)
      childNode.forEachDescendant(handler)
    })
  }

  focus () {
    if (this.isFocused) return
    this.isFocused = true
    this.reconstruct()
  }

  unfocus () {
    if (!this.isFocused) return
    this.isFocused = false
    this.reconstruct()
  }

  fold () {
    if (!this.isDir || this.isFolded) return
    this.isFolded = true
    this.reconstruct()
  }

  unfold () {
    if (!this.isDir || !this.isFolded) return
    this.isFolded = false
    this.reconstruct()
  }

  toggleFold (shouldBeFolded) {
    if (shouldBeFolded) {
      this.fold()
    } else {
      this.unfold()
    }
  }

  highlight () {
    if (!this.isDir || this.isHighlighted) return
    this.isHighlighted = true
    this.reconstruct()
  }

  unhighlight () {
    if (!this.isDir || !this.isHighlighted) return
    this.isHighlighted = false
    this.reconstruct()
  }
}
FileNode.nodes = {}

const bootstrapRootNode = () => {
  FileNode.nodes[ROOT_PATH] = new FileNode({
    path: ROOT_PATH,
    name: Config.projectName,
    isDir: true,
    isFolded: false
  })
}
bootstrapRootNode()

const focusedNodes = () =>
  Object.values(FileNode.nodes).filter(node => node.isFocused).sort(nodeSorter)

export default FileNode
export { ROOT_PATH, bootstrapRootNode, focusedNodes }
